﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft.Win32;
using Airport;
using System.Collections.ObjectModel;
using System.Xml;
using AirportConfigurator.Commanding;
using System.Diagnostics;

namespace AirportConfigurator.ViewModels
{
    class MainWindowViewModel : ViewModelBase
    {
        private IFormatter _formatter = new BinaryFormatter();
        private Stream _stream;

        private Map _airportMap;
        private List<Node> _airportNodes = new List<Node>();
        private List<Edge> _airportEdges = new List<Edge>();
        private List<Surface> _airportSurfaces = new List<Surface>();

        private Runway _selectedRunway;
        private Taxiway _selectedTaxiway;
        private Surface _selectedSurface;
        //private List<Edge> _selectedSurfaceEdges;
        private ObservableCollection<Edge> _selectedSurfaceEdges = new ObservableCollection<Edge>();
        private Edge _selectedEdge;
        //private Dictionary<Edge, bool> _selectedEdgeEndpoint1Neighbors;
        private ObservableCollection<AccessibleEdgeDictionaryItem> _selectedEdgeEndpoint1Neighbors = new ObservableCollection<AccessibleEdgeDictionaryItem>();
        //private Dictionary<Edge, bool> _selectedEdgeEndpoint2Neighbors;
        private ObservableCollection<AccessibleEdgeDictionaryItem> _selectedEdgeEndpoint2Neighbors = new ObservableCollection<AccessibleEdgeDictionaryItem>();

        private Dictionary<string, ObservableCollection<Edge>> _surfaceEdgeDictionary = new Dictionary<string, ObservableCollection<Edge>>();

        private double _scaleFactor;
        private double _defaultAverageSpeed;

        public MainWindowViewModel()
        {
            if (!IsDesignTime)
            {
                WireCommands();
            }
        }

        public Map AirportMap
        {
            get
            {
                return _airportMap;
            }

            set
            {
                _airportMap = value;                
                OnPropertyChanged("AirportMap");
            }
        }

        public List<Node> AirportNodes
        {
            get
            {
                return _airportNodes;
            }

            set
            {
                _airportNodes = value;
                OnPropertyChanged("AirportNodes");
            }
        }

        public List<Edge> AirportEdges
        {
            get
            {
                return _airportEdges;
            }

            set
            {
                _airportEdges = value;
                OnPropertyChanged("AirportEdges");
            }
        }

        public List<Surface> AirportSurfaces
        {
            get
            {
                return _airportSurfaces;
            }

            set
            {
                if (_airportSurfaces != value)
                {
                    _airportSurfaces = value;
                    OnPropertyChanged("AirportSurfaces");
                }
            }
        }

        public Runway SelectedRunway
        {
            get
            {
                return _selectedRunway;
            }

            set
            {
                if (_selectedRunway != value)
                {
                    _selectedRunway = value;
                    OnPropertyChanged("SelectedRunway");
                }
            }
        }

        public Surface SelectedSurface
        {
            get
            {
                return _selectedSurface;
            }

            set
            {
                if (_selectedSurface != value)
                {
                    _selectedSurface = value;
                    
                    var selectedSurfaceEdges = from edge in _airportEdges
                                               where edge.Name.Equals(_selectedSurface.Name)
                                               select edge;
                    
                    //SelectedSurfaceEdges = selectedSurfaceEdges.ToList();
                    SelectedSurfaceEdges = new ObservableCollection<Edge>(selectedSurfaceEdges.ToList());
                    //SelectedSurfaceEdges = SurfaceEdgeDictionary[_selectedSurface.Name];

                    if (SelectedSurfaceEdges.Count > 0)
                    {
                        SelectedEdge = SelectedSurfaceEdges[0];
                    }

                    OnPropertyChanged("SelectedSurface");
                }
            }
        }

        public Dictionary<string, ObservableCollection<Edge>> SurfaceEdgeDictionary
        {
            get
            {
                return _surfaceEdgeDictionary;
            }

            set
            {
                if (_surfaceEdgeDictionary != value)
                {
                    _surfaceEdgeDictionary = value;
                }
            }
        }

        public ObservableCollection<Edge> SelectedSurfaceEdges
        {
            get
            {
                return _selectedSurfaceEdges;
            }

            set
            {
                if (_selectedSurfaceEdges != value)
                {
                    _selectedSurfaceEdges = value;

                    OnPropertyChanged("SelectedSurfaceEdges");
                }
            }
        }

        public Edge SelectedEdge
        {
            get
            {
                return _selectedEdge;
            }

            set
            {
                //if (_selectedEdge != value)
                {
                    _selectedEdge = value;
                    OnPropertyChanged("SelectedEdge");
                    /*
                    if (_selectedEdge != null)
                    {
                        List<AccessibleEdgeDictionaryItem> endpoint1EdgeMap = new List<AccessibleEdgeDictionaryItem>();
                        List<AccessibleEdgeDictionaryItem> endpoint2EdgeMap = new List<AccessibleEdgeDictionaryItem>();

                        //Debug.Write("Endpoint1 Neighbors:");
                        foreach (Edge accessibleNeighbor in _selectedEdge.NeighboringEdges[_selectedEdge.Endpoint1])
                        {
                            //Debug.Write(" " + accessibleNeighbor.ToString() + accessibleNeighbor.Endpoint1.ToString() + accessibleNeighbor.Endpoint2.ToString());                                                        

                            endpoint1EdgeMap.Add(new AccessibleEdgeDictionaryItem
                                {
                                    Key = accessibleNeighbor,
                                    Value = _selectedEdge.AccessibleNeighboringEdges[_selectedEdge.Endpoint1]
                                            .Contains(accessibleNeighbor)
                                });
                        }
                        //Debug.Write("\n");

                        SelectedEdgeEndpoint1Neighbors = new ObservableCollection<AccessibleEdgeDictionaryItem>(endpoint1EdgeMap);

                        //Debug.Write("Endpoint2 Neighbors:");
                        foreach (Edge accessibleNeighbor in _selectedEdge.NeighboringEdges[_selectedEdge.Endpoint2])
                        {
                            //Debug.Write(" " + accessibleNeighbor.ToString() + accessibleNeighbor.Endpoint1.ToString() + accessibleNeighbor.Endpoint2.ToString());                                                        

                            endpoint2EdgeMap.Add(new AccessibleEdgeDictionaryItem
                            {
                                Key = accessibleNeighbor,
                                Value = _selectedEdge.AccessibleNeighboringEdges[_selectedEdge.Endpoint2]
                                        .Contains(accessibleNeighbor)
                            });
                        }
                        //Debug.Write("\n");

                        SelectedEdgeEndpoint2Neighbors = new ObservableCollection<AccessibleEdgeDictionaryItem>(endpoint2EdgeMap);
                    }
                    */
                }
            }
        }

        public ObservableCollection<AccessibleEdgeDictionaryItem> SelectedEdgeEndpoint1Neighbors
        {
            get
            {
                return _selectedEdgeEndpoint1Neighbors;
            }

            set
            {
                if (_selectedEdgeEndpoint1Neighbors != value)
                {
                    _selectedEdgeEndpoint1Neighbors = value;
                    OnPropertyChanged("SelectedEdgeEndpoint1Neighbors");
                }
            }
        }

        public ObservableCollection<AccessibleEdgeDictionaryItem> SelectedEdgeEndpoint2Neighbors
        {
            get
            {
                return _selectedEdgeEndpoint2Neighbors;
            }

            set
            {
                if (_selectedEdgeEndpoint2Neighbors != value)
                {
                    _selectedEdgeEndpoint2Neighbors = value;
                    OnPropertyChanged("SelectedEdgeEndpoint2Neighbors");
                }
            }
        }

        public double ScaleFactor
        {
            get
            {
                return _scaleFactor;
            }

            set
            {
                if (_scaleFactor != value)
                {
                    _scaleFactor = value;
                    OnPropertyChanged("ScaleFactor");
                }
            }
        }

        public double DefaultAverageSpeed
        {
            get
            {
                return _defaultAverageSpeed;
            }

            set
            {
                if (_defaultAverageSpeed != value)
                {
                    _defaultAverageSpeed = value;
                    OnPropertyChanged("DefaultAverageSpeed");
                }
            }
        }

        private void WireCommands()
        {
            LoadAirportLayoutCommand = new RelayCommand(LoadAirportLayoutButton_Click);
            SaveAirportMapCommand = new RelayCommand(SaveAirportMapButton_Click);
            SaveAirportNodesCommand = new RelayCommand(SaveAirportNodesButton_Click);
            SaveAirportEdgesCommand = new RelayCommand(SaveAirportEdgesButton_Click);
            LoadAirportDataCommand = new RelayCommand(LoadAirportDataButton_Click);
            LoadAirportNodesCommand = new RelayCommand(LoadAirportNodesButton_Click);
            LoadAirportEdgesCommand = new RelayCommand(LoadAirportEdgesButton_Click);
            UpdateSelectedEdgeEndpoint1AccessibleEdgeBoolCommand = new RelayCommand(UpdateSelectedEdgeEndpoint1AccessibleEdgeBool_Checked);
            UpdateSelectedEdgeEndpoint2AccessibleEdgeBoolCommand = new RelayCommand(UpdateSelectedEdgeEndpoint2AccessibleEdgeBool_Checked);
            SaveScaleCommand = new RelayCommand(SaveScaleButton_Click);
            SaveDefaultAverageSpeedCommand = new RelayCommand(SaveDefaultAverageSpeedButton_Click);

            LoadAirportLayoutCommand.IsEnabled = true;
            SaveAirportMapCommand.IsEnabled = true;
            SaveAirportNodesCommand.IsEnabled = true;
            SaveAirportEdgesCommand.IsEnabled = true;
            LoadAirportDataCommand.IsEnabled = true;
            LoadAirportNodesCommand.IsEnabled = true;
            LoadAirportEdgesCommand.IsEnabled = true;
            UpdateSelectedEdgeEndpoint1AccessibleEdgeBoolCommand.IsEnabled = true;
            UpdateSelectedEdgeEndpoint2AccessibleEdgeBoolCommand.IsEnabled = true;
            SaveScaleCommand.IsEnabled = true;
            SaveDefaultAverageSpeedCommand.IsEnabled = true;
        }

        public RelayCommand LoadAirportLayoutCommand
        {
            get;
            private set;
        }

        public RelayCommand SaveAirportMapCommand
        {
            get;
            private set;
        }

        public RelayCommand SaveAirportNodesCommand
        {
            get;
            private set;
        }

        public RelayCommand SaveAirportEdgesCommand
        {
            get;
            private set;
        }

        public RelayCommand LoadAirportDataCommand
        {
            get;
            private set;
        }

        public RelayCommand LoadAirportNodesCommand
        {
            get;
            private set;
        }

        public RelayCommand LoadAirportEdgesCommand
        {
            get;
            private set;
        }

        public RelayCommand UpdateSelectedEdgeEndpoint1AccessibleEdgeBoolCommand
        {
            get;
            private set;
        }

        public RelayCommand UpdateSelectedEdgeEndpoint2AccessibleEdgeBoolCommand
        {
            get;
            private set;
        }

        public RelayCommand SaveScaleCommand
        {
            get;
            private set;
        }

        public RelayCommand SaveDefaultAverageSpeedCommand
        {
            get;
            private set;
        }

        private void LoadAirportLayoutButton_Click()
        {
            // Configure open file dialog box
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = "airport"; // Default file name
            dlg.DefaultExt = ".xml"; // Default file extension
            dlg.Filter = "XML Documents|*.xml"; // Filter files by extension            
            dlg.RestoreDirectory = true;

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                // Open Airport Map binary                
                AirportMap = LoadAirportMapFromFile(dlg.FileName);
            }

            // Initialize airport nodes and edges
            InitializeAirportNodesAndEdges();
        }

        private void LoadAirportDataButton_Click()
        {
            // Configure open file dialog box
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = "AirportMap"; // Default file name
            dlg.DefaultExt = ".bin"; // Default file extension
            dlg.Filter = "Bin Files|*.bin"; // Filter files by extension            
            dlg.RestoreDirectory = true;

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                // Open Airport Nodes binary                                
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                AirportMap = (Map)formatter.Deserialize(stream);
                stream.Close();

                List<Surface> airportSurfaces = new List<Surface>();
                airportSurfaces.AddRange(AirportMap.Runways);
                airportSurfaces.AddRange(AirportMap.Taxiways);

                AirportSurfaces = airportSurfaces;
            }
        }

        private void LoadAirportNodesButton_Click()
        {
            // Configure open file dialog box
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = "AirportNodes"; // Default file name
            dlg.DefaultExt = ".bin"; // Default file extension
            dlg.Filter = "Bin Files|*.bin"; // Filter files by extension            
            dlg.RestoreDirectory = true;

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                // Open Airport Nodes binary                                
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                AirportNodes = (List<Node>)formatter.Deserialize(stream);
                stream.Close();
            }
        }

        private void LoadAirportEdgesButton_Click()
        {
            // Configure open file dialog box
            OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
            dlg.FileName = "AirportEdges"; // Default file name
            dlg.DefaultExt = ".bin"; // Default file extension
            dlg.Filter = "Bin Files|*.bin"; // Filter files by extension            
            dlg.RestoreDirectory = true;

            // Show open file dialog box
            Nullable<bool> result = dlg.ShowDialog();

            // Process open file dialog box results
            if (result == true)
            {
                // Open Airport Edges binary                                
                IFormatter formatter = new BinaryFormatter();
                Stream stream = new FileStream(dlg.FileName, FileMode.Open, FileAccess.Read, FileShare.Read);
                AirportEdges = (List<Edge>)formatter.Deserialize(stream);
                stream.Close();
            }

            var surfaceEdgeNames = (from edge in _airportEdges
                                    select edge.Name).Distinct();
            foreach (string name in surfaceEdgeNames)
            {
                var surfaceEdges = from edge in _airportEdges
                                           where edge.Name.Equals(name)
                                           select edge;
                SurfaceEdgeDictionary[name] = new ObservableCollection<Edge>(surfaceEdges.ToList());
            }

        }

        private void SaveAirportMapButton_Click()
        {
            if (AirportMap != null)
            {
                // Configure save file dialog box
                SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
                dlg.FileName = "AirportMap"; // Default file name
                dlg.DefaultExt = ".bin"; // Default file extension
                dlg.Filter = "Binary Files|*.bin"; // Filter files by extension            
                dlg.RestoreDirectory = true;

                // Show save file dialog box
                Nullable<bool> result = dlg.ShowDialog();

                // Process save file dialog box results
                if (result == true)
                {
                    // Save Airport Map binary                                   
                    _stream = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write,
                        FileShare.None);
                    _formatter.Serialize(_stream, AirportMap);
                    _stream.Close();
                }

            }
            else
            {
                // TODO Show error message
            }
        }

        private void SaveAirportNodesButton_Click()
        {
            if (_airportNodes != null)
            {
                // Configure save file dialog box
                SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
                dlg.FileName = "AirportNodes"; // Default file name
                dlg.DefaultExt = ".bin"; // Default file extension
                dlg.Filter = "Binary Files|*.bin"; // Filter files by extension            
                dlg.RestoreDirectory = true;

                // Show save file dialog box
                Nullable<bool> result = dlg.ShowDialog();

                // Process save file dialog box results
                if (result == true)
                {
                    // Save Airport Map binary                                   
                    _stream = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write,
                        FileShare.None);
                    _formatter.Serialize(_stream, _airportNodes);
                    _stream.Close();
                }

            }
            else
            {
                // TODO Show error message
            }
        }

        private void SaveAirportEdgesButton_Click()
        {
            if (_airportEdges != null)
            {
                // Configure save file dialog box
                SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
                dlg.FileName = "AirportEdges"; // Default file name
                dlg.DefaultExt = ".bin"; // Default file extension
                dlg.Filter = "Binary Files|*.bin"; // Filter files by extension            
                dlg.RestoreDirectory = true;

                // Show save file dialog box
                Nullable<bool> result = dlg.ShowDialog();

                // Process save file dialog box results
                if (result == true)
                {
                    // Save Airport Map binary                                   
                    _stream = new FileStream(dlg.FileName, FileMode.Create, FileAccess.Write,
                        FileShare.None);
                    _formatter.Serialize(_stream, _airportEdges);
                    _stream.Close();
                }

            }
            else
            {
                // TODO Show error message
            }
        }        

        private void UpdateSelectedEdgeEndpoint1AccessibleEdgeBool_Checked()
        {
            foreach (AccessibleEdgeDictionaryItem neighbor in _selectedEdgeEndpoint1Neighbors)
            {
                if (neighbor.Value)
                {
                    if (!SelectedEdge.AccessibleNeighboringEdges[SelectedEdge.Endpoint1].Contains(neighbor.Key))
                    {
                        SelectedEdge.AccessibleNeighboringEdges[SelectedEdge.Endpoint1].Add(neighbor.Key);
                    }
                }
                else
                {
                    // Will return false if neightbor.Key doesn't exist, so no problem removing
                    // nonexistant Edge
                    SelectedEdge.AccessibleNeighboringEdges[SelectedEdge.Endpoint1].Remove(neighbor.Key);
                }
            }
        }

        private void UpdateSelectedEdgeEndpoint2AccessibleEdgeBool_Checked()
        {
            foreach (AccessibleEdgeDictionaryItem neighbor in _selectedEdgeEndpoint2Neighbors)
            {
                if (neighbor.Value)
                {
                    if (!SelectedEdge.AccessibleNeighboringEdges[SelectedEdge.Endpoint2].Contains(neighbor.Key))
                    {
                        SelectedEdge.AccessibleNeighboringEdges[SelectedEdge.Endpoint2].Add(neighbor.Key);
                    }
                }
                else
                {
                    // Will return false if neightbor.Key doesn't exist, so no problem removing
                    // nonexistant Edge
                    SelectedEdge.AccessibleNeighboringEdges[SelectedEdge.Endpoint2].Remove(neighbor.Key);
                }
            }
        }

        private void SaveScaleButton_Click()
        {
            if (ScaleFactor >= 0)
            {
                if (_airportEdges != null)
                {
                    // Go through _airportEdges and update each Edge's length with the new scale factor
                    foreach (Edge edge in _airportEdges)
                    {
                        double unscaledLength = edge.Endpoint1.DistanceTo(edge.Endpoint2);
                        double newLength = ScaleFactor * unscaledLength;

                        edge.Length = newLength;
                    }
                }
            }
            else
            {
                ScaleFactor = 1;
            }
        }

        private void SaveDefaultAverageSpeedButton_Click()
        {
            foreach (Edge edge in _airportEdges)
            {
                edge.AverageSpeed = DefaultAverageSpeed;
            }
        }

        public Map LoadAirportMapFromFile(string fileName)
        {
            Tuple<double, double> ORIGINAL_MAP_DIM = new Tuple<double, double>(900, 420);
            //Tuple<double, double> SCALED_MAP_DIM = new Tuple<double, double>(1280, 800);
            Tuple<double, double> SCALED_MAP_DIM = new Tuple<double, double>(1056, 493);
            double SCALE_X = SCALED_MAP_DIM.Item1 / ORIGINAL_MAP_DIM.Item1;
            double SCALE_Y = SCALED_MAP_DIM.Item2 / ORIGINAL_MAP_DIM.Item2;

            ObservableCollection<Runway> runways = new ObservableCollection<Runway>();
            ObservableCollection<Taxiway> taxiways = new ObservableCollection<Taxiway>();
            List<Surface> surfaces = new List<Surface>();

            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(fileName);
            XmlElement xmlRoot = xmlDoc.DocumentElement;
            XmlNodeList xmlRunways = xmlRoot.SelectNodes("/airport/runways/runway");
            XmlNodeList xmlTaxiways = xmlRoot.SelectNodes("/airport/taxiways/taxiway");

            /*
             * <runway direction1="" direction2="">
             *      <maxWeight>
             *      <width>
             *      <arrivals>
             *      <departures>
             *      <labelLocation>
             *      <nodes>
            */
            foreach (XmlNode xmlRunway in xmlRunways)
            {
                Runway newRunway = new Runway();
                newRunway.Direction1 = xmlRunway.Attributes["direction1"].Value;
                newRunway.Direction2 = xmlRunway.Attributes["direction2"].Value;
                newRunway.Name = newRunway.Direction1 + "/" + newRunway.Direction2;
                newRunway.MaxWeight = Convert.ToInt32(xmlRunway.ChildNodes[0].InnerText);
                newRunway.Width = Convert.ToInt32(xmlRunway.ChildNodes[1].InnerText);

                // Set RunwayStatus
                if (xmlRunway.ChildNodes[2].InnerText == "1")
                {
                    // If both Arrival and Departure are true, set RunwayStatus to ARRIVE_DEPART
                    if (xmlRunway.ChildNodes[3].InnerText == "1")
                    {
                        newRunway.CurrentRunwayStatus = RunwayStatus.ARRIVE_DEPART;
                    }
                    else
                    {
                        newRunway.CurrentRunwayStatus = RunwayStatus.ARRIVE;
                    }
                }
                else
                {
                    newRunway.CurrentRunwayStatus = RunwayStatus.DEPART;
                }
                //newRunway.Arrivals = (xmlRunway.ChildNodes[2].InnerText == "0" ? false : true);
                //newRunway.Departures = (xmlRunway.ChildNodes[3].InnerText == "0" ? false : true);

                newRunway.SetLabelLocation(xmlRunway.ChildNodes[4].InnerText, SCALE_X, SCALE_Y);

                foreach (XmlNode xmlRunwayNode in xmlRunway.ChildNodes[5].ChildNodes)
                {
                    newRunway.AddNode(xmlRunwayNode.InnerText, SCALE_X, SCALE_Y);
                }

                // Add Edges to newRunway
                for (int i = 1; i < newRunway.Nodes.Count; i++)
                {
                    Edge newEdge = new Edge(newRunway.Nodes[i - 1], newRunway.Nodes[i]);
                    newEdge.Name = newRunway.Direction1 + "/" + newRunway.Direction2;
                    newRunway.Edges.Add(newEdge);
                }

                /*
                // Set Runway Endpoints
                newRunway.Endpoint1 = newRunway.Nodes.First();
                newRunway.Endpoint2 = newRunway.Nodes.Last();
                */
                surfaces.Add(newRunway);
                runways.Add(newRunway);
            }

            /*
             * <taxiway name="">
             *      <maxWeight>
             *      <width>
             *      <nodes>
            */
            foreach (XmlNode xmlTaxiway in xmlTaxiways)
            {
                Taxiway newTaxiway = new Taxiway();
                newTaxiway.Name = xmlTaxiway.Attributes["name"].Value;
                newTaxiway.MaxWeight = Convert.ToInt32(xmlTaxiway.ChildNodes[0].InnerText);
                newTaxiway.Width = Convert.ToInt32(xmlTaxiway.ChildNodes[1].InnerText);

                foreach (XmlNode xmlTaxiwayNode in xmlTaxiway.ChildNodes[2].ChildNodes)
                {
                    newTaxiway.AddNode(xmlTaxiwayNode.InnerText, SCALE_X, SCALE_Y);
                }

                // Add Edges to newTaxiway
                for (int i = 1; i < newTaxiway.Nodes.Count; i++)
                {
                    Edge newEdge = new Edge(newTaxiway.Nodes[i - 1], newTaxiway.Nodes[i]);
                    newEdge.Name = newTaxiway.Name;
                    newTaxiway.Edges.Add(newEdge);
                }

                /*
                // Set Taxiway Endpoints
                newTaxiway.Endpoint1 = newTaxiway.Nodes.First();
                newTaxiway.Endpoint2 = newTaxiway.Nodes.Last();
                */
                surfaces.Add(newTaxiway);
                taxiways.Add(newTaxiway);
            }

            /*
            foreach(Runway runway in runways) {
                Console.WriteLine(runway);
            }

            foreach(Taxiway taxiway in taxiways) {
                Console.WriteLine(taxiway);
            }

            Console.ReadLine();
            */
            AirportSurfaces = surfaces;

            return new Map(runways, taxiways, new ObservableCollection<Gate>());
        }

        private void InitializeAirportNodesAndEdges()
        {
            try
            {
                foreach (Runway runway in _airportMap.Runways)
                {
                    // Iterate through this Runway's Nodes
                    for (int i = 0; i < runway.Nodes.Count; i++)
                    {
                        Node workingNode = new Node();
                        bool found = false;

                        // Check if the current Node is already in _airportNodes
                        for (int j = 0; j < _airportNodes.Count; j++)
                        {
                            // If the current Node is already in _airportNodes,
                            // set workingNode to the existing Node
                            if (runway.Nodes[i] == _airportNodes[j])
                            {
                                workingNode = _airportNodes[j];
                                found = true;
                                break;
                            }
                        }

                        // If the current Node was not found in _airportNodes,
                        // set workingNode to a copy of the current Node and
                        // add workingNode to _airportNodes
                        if (!found)
                        {
                            workingNode = new Node(runway.Nodes[i]);
                            AirportNodes.Add(workingNode);
                        }

                        // Iterate over _airportNodes to find the Node preceding
                        // workingNode in the runway and add workingNode and
                        // the preceding Node to each other's Neighbors Lists
                        for (int k = 0; k < _airportNodes.Count; k++)
                        {
                            if (i > 0)
                            {
                                if (runway.Nodes[i - 1] == _airportNodes[k])
                                {
                                    // Add the previous Node to workingNode's Neighbors
                                    workingNode.Neighbors.Add(_airportNodes[k]);

                                    // Add workingNode to the previous Node's Neighbors
                                    AirportNodes[k].Neighbors.Add(workingNode);
                                    break;
                                }
                            }
                        }
                    }

                    // Add this Runway's Edges to _airportEdges
                    AirportEdges.AddRange(runway.Edges);
                }

                foreach (Taxiway taxiway in _airportMap.Taxiways)
                {
                    // Iterate through this Taxiway's Nodes
                    for (int i = 0; i < taxiway.Nodes.Count; i++)
                    {
                        Node workingNode = new Node();
                        bool found = false;

                        // Check if the current Node is already in _airportNodes
                        for (int j = 0; j < _airportNodes.Count; j++)
                        {
                            // If the current Node is already in _airportNodes,
                            // set workingNode to the existing Node
                            if (taxiway.Nodes[i] == _airportNodes[j])
                            {
                                workingNode = _airportNodes[j];
                                found = true;
                                break;
                            }
                        }

                        // If the current Node was not found in _airportNodes,
                        // set workingNode to a copy of the current Node and
                        // add workingNode to _airportNodes
                        if (!found)
                        {
                            workingNode = new Node(taxiway.Nodes[i]);
                            AirportNodes.Add(workingNode);
                        }

                        // Iterate over _airportNodes to find the Node preceding
                        // workingNode in the runway and add workingNode and
                        // the preceding Node to each other's Neighbors Lists
                        for (int k = 0; k < _airportNodes.Count; k++)
                        {
                            if (i > 0)
                            {
                                if (taxiway.Nodes[i - 1] == _airportNodes[k])
                                {
                                    // Add the previous Node to workingNode's Neighbors
                                    workingNode.Neighbors.Add(_airportNodes[k]);

                                    // Add workingNode to the previous Node's Neighbors
                                    AirportNodes[k].Neighbors.Add(workingNode);
                                    break;
                                }
                            }
                        }
                    }

                    // Add this Taxiway's Edges to _airportEdges
                    AirportEdges.AddRange(taxiway.Edges);
                }


                // Connect the airport's Edges to each other
                foreach (Edge edge in _airportEdges)
                {
                    var endpoint1Matches = from otherEdge in _airportEdges
                                           where (edge != otherEdge) && ((edge.Endpoint1 == otherEdge.Endpoint1) ^ (edge.Endpoint1 == otherEdge.Endpoint2))
                                           select otherEdge;
                    /*
                    Debug.Write("Endpoint1 Edges From Query:");
                    foreach (Edge debugEdge in endpoint1Matches)
                    {
                        Debug.Write(" " + debugEdge.ToString() + debugEdge.Endpoint1.ToString() + debugEdge.Endpoint2.ToString());
                    }
                    Debug.Write("\n");
                    */
                    edge.NeighboringEdges[edge.Endpoint1] = endpoint1Matches.ToList<Edge>();

                    // Default all neighboring edges to accessible
                    edge.AccessibleNeighboringEdges[edge.Endpoint1] = endpoint1Matches.ToList<Edge>();
                    /*
                    Debug.Write("Endpoint1 Edges:");
                    foreach (Edge debugEdge in edge.NeighboringEdges[edge.Endpoint1])
                    {
                        Debug.Write(" " + debugEdge.ToString() + debugEdge.Endpoint1.ToString() + debugEdge.Endpoint2.ToString());
                    }
                    Debug.Write("\n");
                    */
                    var endpoint2Matches = from otherEdge in _airportEdges
                                           where (edge != otherEdge) && ((edge.Endpoint2 == otherEdge.Endpoint1) ^ (edge.Endpoint2 == otherEdge.Endpoint2))
                                           select otherEdge;
                    //edge.NeighboringEdges[edge.Endpoint2].AddRange(endpoint2Matches);
                    edge.NeighboringEdges[edge.Endpoint2] = endpoint2Matches.ToList<Edge>();

                    // Default all neighboring edges to accessible
                    edge.AccessibleNeighboringEdges[edge.Endpoint2] = endpoint2Matches.ToList<Edge>();
                    /*
                    Debug.Write("Endpoint2 Edges:");
                    foreach (Edge debugEdge in edge.NeighboringEdges[edge.Endpoint2])
                    {
                        Debug.Write(" " + debugEdge.ToString() + debugEdge.Endpoint1.ToString() + debugEdge.Endpoint2.ToString());
                    }
                    Debug.Write("\n");
                    */                    
                }

                // DEBUGGING
                /*
                foreach (Node node in _airportNodes)
                {
                    Debug.WriteLine("Node: {0}", node);
                    Debug.Write("   Neighbors: ");
                    foreach (Node accessibleNeighbor in node.Neighbors)
                    {
                        Debug.Write(accessibleNeighbor);
                        Debug.Write(" ");
                    }
                    Debug.Write("\n");
                }   
                
                foreach (Edge edge in _airportEdges)
                {
                    Debug.Write(edge.Name);
                    Debug.Write(" Endpoint1:");
                    foreach (Edge neighboringEdge1 in edge.NeighboringEdges[edge.Endpoint1])
                    {
                        Debug.Write(" " + neighboringEdge1.Name);
                    }
                    Debug.Write(" Endpoint2:");
                    foreach (Edge neighboringEdge2 in edge.NeighboringEdges[edge.Endpoint2])
                    {
                        Debug.Write(" " + neighboringEdge2.Name);
                    }
                    Debug.Write("\n");
                }
                */
            }
            catch (Exception ex)
            {
                Debug.WriteLine("AirportService: {0}", ex.Message);
            }
        }


        public class AccessibleEdgeDictionaryItem
        {
            public Edge Key { get; set; }

            public bool Value
            {
                get;

                set;
            }

            public static bool operator ==(AccessibleEdgeDictionaryItem a, AccessibleEdgeDictionaryItem b)
            {
                if (System.Object.ReferenceEquals(a, b))
                {
                    return true;
                }

                if (((object)a == null) || ((object)b == null))
                {
                    return false;
                }

                return a.Key == b.Key;
            }

            public static bool operator !=(AccessibleEdgeDictionaryItem a, AccessibleEdgeDictionaryItem b)
            {
                return !(a == b);
            }
        }
    }
}
